iT邦幫忙

2022 iThome 鐵人賽

DAY 17
1
Software Development

這些年,我們早該學會的Spring Framework系列 第 29

Day29 - Declarative transaction management (5)

  • 分享至 

  • xImage
  •  

Review

昨日我們講解當Transaction出現Concurrent時會造成什麼樣的問題,透過Spring提供的Isolation屬性可以解決對應的問題。

今日我們將介紹@Transactional當中另一個重要的屬性Propagation

事務傳播 Propagation

當事務中還有事務時,子事務是否要跟大事務共用一個事務。例如transferMoney是一個大事務,子事務addMoney、minusMoney是否要與transferMoney共用事務。

@Transactional()
public void transferMoney(String from, String to , Integer amount) throws FileNotFoundException {
    accountDao.addMoney(to,amount);
    accountDao.minusMoney(from,amount);
    System.out.println("轉帳完成:"+from+" to "+to+" 金額:"+amount);
}

Propagation Types

Types Description
REQUIRED Support a current transaction, create a new one if none exists.
REQUIRED_NEW Create a new transaction, and suspend the current transaction if one exists.
SUPPORTS Support a current transaction, execute non-transactionally if none exists.
NOT_SUPPORTED Execute non-transactionally, suspend the current transaction if one exists.
MANATORY Support a current transaction, throw an exception if none exists.
NEVER Execute non-transactionally, throw an exception if a transaction exists.
NESTED Execute within a nested transaction if a current transaction exists, behave like REQUIRED otherwise.

Callee 皆為REQUIRED

@Service
public class AccountService {
    @Autowired
    private AccountDao accountDao;

    @Transactional(propagation = Propagation.REQUIRED)
    public void transferMoney(String from, String to , Integer amount) {
        //Callee(1)
        accountDao.addMoney(to,amount);
        //Callee(2)
        accountDao.minusMoney(from,amount);

        System.out.println("轉帳完成:"+from+" to "+to+" 金額:"+amount);
    }
}
@Repository
public class AccountDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Transactional(propagation = Propagation.REQUIRED)
    public void addMoney(String accountId, Integer amount) {
        String sql = "UPDATE ACCOUNT SET BALANCE = BALANCE +? WHERE ACCOUNT_ID = ?";
        jdbcTemplate.update(sql, amount, accountId);
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void minusMoney(String accountId, Integer amount) {
        String sql = "UPDATE ACCOUNT SET BALANCE = BALANCE -? WHERE ACCOUNT_ID = ?";
        jdbcTemplate.update(sql, amount, accountId);
        int i = 10/0;
    }
}
@Test
public void testDay29()  {
    ApplicationContext ioc = new ClassPathXmlApplicationContext("bean29.xml");
    AccountService service = ioc.getBean(AccountService.class);
    service.transferMoney("1","2",100);
}

Result
Transaction rollback帳戶皆不變
https://ithelp.ithome.com.tw/upload/images/20221014/20128084tGv5WPBt7V.jpg

Callee(2)為REQUIRED,Callee(1)為REQUIRED_NEW

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void addMoney(String accountId, Integer amount) {
    String sql = "UPDATE ACCOUNT SET BALANCE = BALANCE +? WHERE ACCOUNT_ID = ?";
    jdbcTemplate.update(sql, amount, accountId);
}

Result
由於addMoney是開新的Transaction所以完成就代表該Transaction commit,所以callee(2)的exception影響不到callee(1)
https://ithelp.ithome.com.tw/upload/images/20221014/20128084wIN3Rcp8vx.jpg

caller 拋異常,caller皆為REQUIRED_NEW

 @Transactional(propagation = Propagation.REQUIRED)
public void transferMoney(String from, String to , Integer amount) {
    accountDao.addMoney(to,amount);
    accountDao.minusMoney(from,amount);
    int i = 10/0;
    System.out.println("轉帳完成:"+from+" to "+to+" 金額:"+amount);
}
 @Transactional(propagation = Propagation.REQUIRES_NEW)
public void addMoney(String accountId, Integer amount) {
    String sql = "UPDATE ACCOUNT SET BALANCE = BALANCE +? WHERE ACCOUNT_ID = ?";
    jdbcTemplate.update(sql, amount, accountId);
}

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void minusMoney(String accountId, Integer amount) {
    String sql = "UPDATE ACCOUNT SET BALANCE = BALANCE -? WHERE ACCOUNT_ID = ?";
    jdbcTemplate.update(sql, amount, accountId);
}

Result
由於子事務皆是REQUIRED_NEW所以結束即代表Transaction commit 所以caller最後拋的異常並不影響子事務。
https://ithelp.ithome.com.tw/upload/images/20221014/20128084XiM3GbPRWa.jpg

Caller調用自身方法只會有一個Transaction

 @Transactional(propagation = Propagation.REQUIRED)
public void transferMoney2(String from, String to , Integer amount) {
    addMoney(to,amount);
    minusMoney(from,amount);
    int i = 10/0;
    System.out.println("轉帳完成:"+from+" to "+to+" 金額:"+amount);
}
 @Transactional(propagation=Propagation.REQUIRES_NEW)
public void addMoney(String to,Integer amount){
    accountDao.addMoney(to,amount);
}
 @Transactional(propagation=Propagation.REQUIRES_NEW)
public void minusMoney(String from,Integer amount){
    accountDao.minusMoney(from,amount);
}
@Repository
public class AccountDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;

public void addMoney(String accountId, Integer amount) {
    String sql = "UPDATE ACCOUNT SET BALANCE = BALANCE +? WHERE ACCOUNT_ID = ?";
    jdbcTemplate.update(sql, amount, accountId);
}
public void minusMoney(String accountId, Integer amount) {
    String sql = "UPDATE ACCOUNT SET BALANCE = BALANCE -? WHERE ACCOUNT_ID = ?";
    jdbcTemplate.update(sql, amount, accountId);
}
@Test
public void testDay29()  {
    ApplicationContext ioc = new ClassPathXmlApplicationContext("bean29.xml");
    AccountService service = ioc.getBean(AccountService.class);
    service.transferMoney2("1","2",100);
}

Result
AccountService的transferMoney2 method調用本地方法非用代理對象,故只會有一個Transaction
https://ithelp.ithome.com.tw/upload/images/20221015/20128084CefjkW2gUA.jpg

Reference


上一篇
Day28 - Declarative transaction management (4)
下一篇
Day30 - Declarative transaction management (6)
系列文
這些年,我們早該學會的Spring Framework30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言